﻿#region

using System;
using System.Collections.Generic;
using System.Linq;

#endregion

namespace Torche
{

    #region Комментарий

    //"Садовник заботится чтобы ягод было не меньше 5, но не
    //больше 50 – для этого он устраивает отстрел лишних ягод из ружья случайным
    //образом каждые 5 минут" - из этого не очень очевидно следует, сколько надо убивать торчебузов
    //если только лишние - то их будет скорее всего не меньше 50 через некоторое время после старта
    //поэтому я сделал так, чтобы садовник убивал случайное кол-во торчебузов так, чтобы оставшееся их кол-во
    //лежало в допустимых пределах от min до max (KillRandomTorcheBooses)

    #endregion

    /// <summary>Классы по торчебузам
    /// </summary>
    internal class TorcheClasses
    {
        #region константы

        /// <summary>Состояния объектов
        /// </summary>
        public enum TorcheStates
        {
            /// <summary>просто живой
            /// </summary>
            Alive,

            /// <summary>просто мертвый
            /// </summary>
            Dead,

            /// <summary>растет
            /// </summary>
            Growing,

            /// <summary>цветет
            /// </summary>
            Flowering,

            /// <summary>созревает
            /// </summary>
            Ripening,

            /// <summary>плодоносит
            /// </summary>
            Fruiting,

            /// <summary>собирает нектар
            /// </summary>
            GettingNectar,

            /// <summary>летит к торчебузу
            /// </summary>
            FlyingToTorcheboose,


            /// <summary>летит разгружаться
            /// </summary>
            FlyingToHive,

            /// <summary>Разгружается
            /// </summary>
            Unloading 
        }

        /// <summary>Ширина игрового поля
        /// </summary>
        public const int GardenWidth = 100;

        /// <summary>
        /// Высота игрового поля
        /// </summary>
        public const int GardenHeight = 100;

        /// <summary>Длительность в секундах
        /// Длительность одного цикла торчебуза (напр. созревания) в секундах
        /// </summary>
        public const int TorcheBooseCycleSec = 30;

        /// <summary>Минимальное кол-во торчебузов
        /// </summary>
        public const int TorcheBooseCountMin = 5;

        /// <summary>Максимальное кол-во торчебузов
        /// </summary>
        public const int TorcheBooseCountMax = 50;

        /// <summary>Каждые нцать минут садовник отстреливает лишние ягоды
        /// </summary>
        public const int ShootPeriodSec = 300;

        /// <summary>Время на сбор нектара
        /// Сколько времени требуется пчеле на сбор нектара с цветка
        /// </summary>
        public const int GetNectarDurationSec = 15;

        /// <summary>Время разгрузки
        /// Время, которое требуется пчеле, чтобы выгрузить нектар в улей
        /// </summary>
        public const int UnloadDurationSec = 10;

        /// <summary>Кол-во пчел
        /// Количество пчел, которые должны собирать нектар
        /// если садовник отстрелил торчебуз с пчелой(пчелами)
        /// добавляем пчелу(пчел) на поле
        /// </summary>
        public const int BeeCount = 3;

        /// <summary>Кол-во новых торчебузов
        /// После удачного плодоношения появляются
        /// новые торчебузы вместо старого
        /// </summary>
        public const int Harvest = 2;

        /// <summary>Период обновления в миллисекундах
        /// </summary>
        public const int UpdatePeriodMSec = 1000;

        /// <summary>Кол-во торчебузов на клетку
        /// </summary>
        public const int MaxCountTorcheBooseInCell = 1;

        /// <summary>Кол-во пчел на клетку
        /// </summary>
        public const int MaxCountTorcheBeeInCell = 10;

        /// <summary>Максимальное кол-во объектов в клетке вообще
        /// </summary>
        public const int MaxCountTorcheObjectsInCell = 20;

        #endregion

        #region Nested type: Coord

        /// <summary>Класс координат
        /// для запоминания местоположения в сетке
        /// </summary>
        public class Coord
        {
            /// <summary>По одной оси
            /// </summary>
            public int X { get; set; }

            /// <summary>По другой оси
            /// </summary>
            public int Y { get; private set; }

            /// <summary>Создаем координаты
            /// </summary>
            /// <param name="x">по одной оси</param>
            /// <param name="y">по другой оси</param>
            public Coord(int x, int y)
            {
                X = x;
                Y = y;
            }

            /// <summary>Создаем координаты
            /// </summary>
            public Coord()
            {
            }
        }

        #endregion

        #region Nested type: TorcheBase

        /// <summary>Базовый торчекласс
        /// </summary>
        public class TorcheBase
        {
            /// <summary>Рисуем
            /// метод будет переопределяться наследниками
            /// </summary>
            public virtual void Draw()
            {
            }

            /// <summary>Обновить объект
            /// вызывается раз в UpdatePeriodMSec миллисекунд
            /// </summary>
            public virtual void Update()
            {
                Draw();
            }
        }

        #endregion

        #region Nested type: TorcheBee

        /// <summary>Пчела
        /// летает по саду и собирает нектар        
        /// </summary>
        public class TorcheBee : TorcheObject
        {
            /// <summary>Создаем торчепчелку
            /// </summary>
            /// <param name="garden">сад, в котором пчелка будет летать</param>
            /// <param name="coord">клетка, в которой пчелка появиццо. По идее - та же, где улей</param>
            public TorcheBee(TorcheGarden garden, Coord coord) : base(garden, coord)
            {
                state = TorcheStates.FlyingToTorcheboose;
            }

            /// <summary>Ищем подходящий торчебуз
            /// </summary>
            /// <returns></returns>
            private Coord FoundNextTorcheBoose()
            {
                var result = new Coord();

                //просматриваем ближайшие клетки в поисках цветущего или
                //растущего торчебуза, возвращаем координаты его клетки

                return result;
            }

            public override void Update()
            {
                base.Update();
                switch (State)
                {
                    //в зависимости от статуса пчелка ищет новый торчебуз или летит обратно и тп
                    //когда загружаемся - увеличиваем NectarPercent на соотв. величину
                    //когда разгружаемся - наоборот. Загрузились\Разгрузились - полетели.
                    //вычисляем расстояние до цели, устанавливаем TimeToTargetSec и потом уменьшаем его
                    //возможность собрать нектар проверяем с каждым апдейтом - смотрим на статус торчебуза
                }
            }

            /// <summary>Сколько процентов нектара собрано
            /// </summary>
            public int NectarPercent { get; protected set; }

            /// <summary>Сколько осталось до пункта назначения (торчебуза, улья и т.п.)
            /// </summary>
            public int TimeToTargetSec { get; protected set; }

            /// <summary>Жива ли пчелка
            /// </summary>
            /// <returns></returns>
            public override bool IsAlive()
            {
                //здесь перечислим все состояния пчелы при которых она считается живой
                throw new NotImplementedException();
            }
        }

        #endregion

        #region Nested type: TorcheBoose

        /// <summary>Собственно, торчебуз
        /// </summary>
        public class TorcheBoose : TorcheObject
        {
            /// <summary>Создаем торчебузик
            /// </summary>
            /// <param name="garden">сад, в котором торчебуз</param>
            /// <param name="coord">клетка, в которой торчебуз</param>
            public TorcheBoose(TorcheGarden garden, Coord coord) : base(garden, coord)
            {
            }

            /// <summary>Жив ли торчебуз
            /// </summary>
            /// <returns>да нет</returns>
            public override bool IsAlive()
            {
                switch (State)
                {
                    case TorcheStates.Flowering:
                    case TorcheStates.Fruiting:
                    case TorcheStates.Growing:
                    case TorcheStates.Ripening:
                        return true;
                    default:
                        return false;
                }
            }

            public override void Update()
            {
                //если пришло время менять статус - меняем
                //пришло время умирать - умираем
                //хотим добавить новых торчей - обращаемся к соседним клеткам и,
                //если это возможно, добавляем
                base.Update();
            }

            public int TimeToChangeStatus { get; protected set; }
        }

        #endregion

        #region Nested type: TorcheCell

        /// <summary>Ячейка на карте
        /// </summary>
        public class TorcheCell : TorcheBase
        {
            /// <summary>объекты в клетке
            /// </summary>
            public readonly List<TorcheObject> objects = new List<TorcheObject>();

            /// <summary>Добавить в клетку объект
            /// </summary>
            /// <param name="obj">добавляемый объект</param>
            /// <returns>добавился ли объект</returns>
            public bool AddObject(TorcheObject obj)
            {
                //Максимальное кол-во объектов этого типа в клетке
                int maxCountOfThisObjects = MaxCountTorcheObjectsInCell;

                if (obj is TorcheBee)
                    maxCountOfThisObjects = MaxCountTorcheBeeInCell;
                if (obj is TorcheBoose)
                    maxCountOfThisObjects = MaxCountTorcheBooseInCell;

                //Если не достигнуто максимальное значение - добавляем
                if (GetCountOfObjects(obj.GetType()) < maxCountOfThisObjects)
                {
                    objects.Add(obj);
                    return true;
                }

                return false;
            }

            /// <summary>Возвращаем кол-во объектов в клетке заданного типа
            /// </summary>
            /// <param name="type">искомый тип</param>
            /// <returns>кол-во объектов</returns>
            public int GetCountOfObjects(Type type)
            {
                return objects.Count(torcheObject => torcheObject.GetType() == type);
            }
        }

        #endregion

        #region Nested type: TorcheGarden

        /// <summary>Сад (игровое поле)
        /// </summary>
        public class TorcheGarden : TorcheBase
        {
            /// <summary>клетки поля
            /// </summary>
            private readonly List<List<TorcheCell>> cells;

            /// <summary>Создаем сад
            /// </summary>
            /// <param name="heigh">кол-во клеток по высоте</param>
            /// <param name="width">кол-во клеток по ширине</param>
            public TorcheGarden(int heigh, int width)
            {
                //Создаем клетки поля
                cells = new List<List<TorcheCell>>();
                for (int i = 0; i < heigh; i++)
                {
                    var tmp = new List<TorcheCell>(width);
                    for (int j = 0; j < width; j++)
                    {
                        tmp.Add(new TorcheCell());
                    }
                    cells.Add(tmp);
                }
            }

            /// <summary>Кол-во объектов
            /// например передаем тип торчебузов
            /// получаем кол-во торчебузов на поле
            /// </summary>
            /// <param name="type"></param>
            /// <returns></returns>
            private int GetCountOfObject(Type type)
            {
                //пробегаем по спискам и считаем
                throw new NotImplementedException();
            }

            /// <summary>Кол-во торчебузов на поле
            /// </summary>
            /// <returns>кол-во торчебузов</returns>
            public int GetTorcheBoosesCount()
            {
                return GetCountOfObject(typeof (TorcheBoose));
            }

            /// <summary>кол-во пчел на поле
            /// </summary>
            /// <returns>кол-во пчел</returns>
            public int GetTorcheBeeCount()
            {
                return GetCountOfObject(typeof (TorcheBee));
            }

            public override void Update()
            {
                base.Update();
                //проходим по всем объектам, и те, которые умерли - уничтожаем
            }
        }

        #endregion

        #region Nested type: TorcheGardener

        /// <summary>Садовник
        /// бегает по саду и отстреливает лишние торчебузы
        /// </summary>
        public class TorcheGardener : TorcheObject
        {
            /// <summary>Создаем садовника
            /// </summary>
            /// <param name="garden">сад садовника</param>
            /// <param name="coord">клетка, в которой появиццо садовник</param>
            public TorcheGardener(TorcheGarden garden, Coord coord)
                : base(garden, coord)
            {
                TimeToKillSec = ShootPeriodSec;
            }

            /// <summary>Время до следующей охоты
            /// </summary>
            public int TimeToKillSec { get; private set; }

            public override void Update()
            {
                base.Update();

                TimeToKillSec--; //время до охоты на торчебузов

                //По саду бегает садовник и сажает новые ягоды
                PutTorcheBooses();

                //И убивает лишние торчебузы
                KillRandomTorcheBooses();
            }

            /// <summary>Пришло время убивать?
            /// </summary>
            /// <returns>пришло</returns>
            private bool IsTimeToKill()
            {
                return TimeToKillSec <= 0;
            }

            /// <summary>Убиваем случайное кол-во торчебузов
            /// убиваем случайное кол-во торчебузов так,
            /// чтобы осталось торчебузов между min и max
            /// </summary>
            /// <returns>убит?</returns>
            private bool KillRandomTorcheBooses()
            {
                if (IsTimeToKill())
                {
                    var r = new Random();
                    int countTorch = Garden.GetTorcheBoosesCount();

                    //случайное число от минимально до максимально возможного кол-ва торчебузов
                    //для убийства
                    int killCount = r.Next(countTorch - TorcheBooseCountMax, countTorch - TorcheBooseCountMin);

                    for (int i = 0; i < killCount; i++)
                    {
                        KillRandomTorcheBoose();
                    }
                    return killCount > 0;
                }
                return false;
            }

            /// <summary>Кидаем ягоды на поле
            /// </summary>
            private int PutTorcheBooses()
            {
                int i = 0;
                while (Garden.GetTorcheBoosesCount() < TorcheBooseCountMax)
                {
                    if (!PutTorcheBooseToRandomCell()) break;
                    i++;
                }
                return i;
            }

            /// <summary>Убить торчебуз на случайной клетке
            /// </summary>
            private void KillRandomTorcheBoose()
            {
                //Выбираем случайную клетку
                //если нет торчебуза - выбираем другую
                //если есть:
                //вызываем торчебуз.Kill()
                //если есть пчелы при этом для всех пчел.kill()
                //если есть обезъянки, кроты и проч - по усмотрению
                throw new NotImplementedException();
            }

            /// <summary>Кинуть торчебуз на случайную клетку
            /// </summary>
            /// <returns>добавился ли</returns>
            private bool PutTorcheBooseToRandomCell()
            {
                //выбираем случайную клетку
                //проверяем, можно ли добавить в нее торчебуз
                //добавляем
                throw new NotImplementedException();
            }
        }

        #endregion

        #region Nested type: TorcheHive

        /// <summary>Торчеулей
        /// из него вылетают пчелы
        /// и в него приносят нектар
        /// </summary>
        public class TorcheHive : TorcheObject
        {
            /// <summary>Создаем улей
            /// </summary>
            /// <param name="garden">сад, в котором улей</param>
            /// <param name="coord">клетка, в которой улей</param>
            public TorcheHive(TorcheGarden garden, Coord coord) : base(garden, coord)
            {
            }

            /// <summary>Добавляем пчел, если их 
            /// </summary>
            public override void Update()
            {
                base.Update();
                int beeCount = Garden.GetTorcheBeeCount();
                while (beeCount < MaxCountTorcheBeeInCell)
                {
                    AddBee();
                }
            }

            /// <summary>Добавить пчелу
            /// пчела добавляется в клетку улья, потом полетит куда ей надо
            /// </summary>
            /// <returns>добавилась ли</returns>
            private bool AddBee()
            {
                //добавляем к клетке пчелу
                throw new NotImplementedException();
            }
        }

        #endregion

        #region Nested type: TorcheObject

        /// <summary>Базовый торчекласс для всех объектов на карте
        /// </summary>
        public class TorcheObject : TorcheBase
        {
            protected TorcheStates state = TorcheStates.Alive;

            /// <summary>Создаем объект на карте
            /// </summary>
            /// <param name="garden">сад, в котором объект</param>
            /// <param name="coord">клетка, в которой объект</param>
            public TorcheObject(TorcheGarden garden, Coord coord)
            {
                Coordinates = coord;
                Garden = garden;
            }

            /// <summary>Где расположен объект
            /// </summary>
            public Coord Coordinates { get; protected set; }

            /// <summary>Сад, в котором объект
            /// </summary>
            public TorcheGarden Garden { get; protected set; }

            /// <summary>Состояние объекта
            /// </summary>
            public TorcheStates State
            {
                get { return state; }
            }

            /// <summary>Общий возраст объекта
            /// указывает, сколько прожил объект
            /// для торчебуза это будет всегда 
            /// величина не большая 1 периоду * кол-во периодов
            /// для торчепчел - зависит от ее везучести
            /// для садовника и улья - времени работы программы
            /// </summary>
            public int Age { get; protected set; }

            public override void Update()
            {
                base.Update();
                Age += 1;
            }

            /// <summary>Убить торча
            /// </summary>
            public virtual void Kill()
            {
                state = TorcheStates.Dead;
            }

            /// <summary>Пациент скорее жив, чем мертв
            /// </summary>
            /// <returns></returns>
            public virtual bool IsAlive()
            {
                return state == TorcheStates.Alive;
            }
        }

        #endregion
    }
}